按键滤波程序

200次阅读

共计 5233 个字符,预计需要花费 14 分钟才能阅读完成。

一、扫描按键状态

KEY_ScanValue 函数用于扫描按键状态

void KEY_ScanValue(void)
{
    // 将 PB0-PB3 设置为输入,其他位不变
    key.scanBuf &= 0x00; // 读取 PB0-PB3 的状态,低电平有效
    DDRB &= ~0x1E;       // 将 PB1-PB3 设为输入
    PORTB |= 0x1E;       // 使能上拉电阻,PB1-PB3 为高电平
    // 清除当前扫描的按键位
    PORTB &= ~(1 << key.bit); // 选择当前要扫描的按键(设置为低电平)// 更新扫描缓冲区
    // key.scanBuf <<= 1;

    // 将 scanBuf 的值左移 1 位,为下一次扫描腾出位置
    key.scanBuf |= (~PINB & 0x1E); // 读取 PB1-PB4 的状态,低电平有效

    // 检查是否扫描完所有按键
    if (++key.bit > 3)
    {
        key.bit = 0;               // 重置 key.bit 为 0
        key.readBuf = key.scanBuf; // 将当前的 scanBuf 值存入 readBuf
        key.scanBuf = 0;           // 清空 scanBuf,为下一次扫描做准备
        key.read = 1;              // 设置 read 标志,表示新数据可用
    }
}

逐步解释

  1. 初始化和配置
    • key.scanBuf &= 0x00;:清空扫描缓冲区,确保没有旧数据。
    • DDRB &= ~0x1E;:将 PB1、PB2 和 PB3 设置为输入模式。
    • PORTB |= 0x1E;:使能 PB1、PB2 和 PB3 的上拉电阻,确保它们在未按下时为高电平。
  2. 选择当前按键
    • PORTB &= ~(1 << key.bit);:将当前扫描的按键设置为低电平,允许读取其状态。
  3. 更新扫描缓冲区
    • key.scanBuf |= (~PINB & 0x1E);:读取 PB1、PB2 和 PB3 的状态,低电平有效。通过取反 PINB,可以得到当前按键的状态,并更新 scanBuf
  4. 按键扫描完成检查
    • if (++key.bit > 3):如果扫描到第四个按键(即 PB3),则重置 key.bit 为 0,准备下一轮扫描。
    • key.readBuf = key.scanBuf;:将当前的扫描结果存入 readBuf,以便后续处理。
    • key.scanBuf = 0;:清空 scanBuf,为下一次扫描做准备。
    • key.read = 1;:设置标志位,表示新数据已准备好。

二、按键消抖

void KEY_GetValue(void)
{
    unsigned char ucBuf;

    if (key.read)
    {
        key.read = 0;
        if (key.readBuf != key.readBakBuf)
        {
            key.readBakBuf = key.readBuf; // 更新键值
            key.delay = 0x00;             // 清零按键计数器
            key.newTimes = 0x00;          // 新键按下计时
            key.disable = 0;              // 清除按键禁能标志(键值更新须处理按键)}
        else
        {
            key.delay++; // 按键消抖

            if (key.delay > key.ditherTimes)
            {                               // Key Delay
                key.validBuf = key.readBuf; // 读取有效键值
                if (key.accelMode)
                { // 新键按下总时间

                    ucBuf = key.newTimes;
                }
                else
                { // 按键按下总时间
                    ucBuf = key.onTimes;
                }

                if (ucBuf > 20)
                {                        // 高速(6 秒以上 由扫描次数决定速度)key.decode = 1;      // 按键译码申请
                    key.ditherTimes = 3; // 提高按键读取速度
                    key.delay = 0x00;
                }
                else if (ucBuf > 10)
                {                        // 中速(3 秒以上 由扫描次数决定速度)key.decode = 1;      // 按键译码申请
                    key.ditherTimes = 5; // 提高按键读取速度
                    key.delay = 0x00;
                }
                else
                {                         // 低速(正常操作 由定时器决定速度)key.ditherTimes = 10; // 恢复按键速度
                    if (key.disable)
                    {key.delay = key.ditherTimes;}
                    else
                    {
                        key.decode = 1;  // 按键译码申请
                        key.disable = 1; // 按键限时防止连续处理
                        key.delay = 0x00;
                    }
                }
            }
        }
    }
}
KEY_GetValue 函数用于处理按键输入和消抖

逐步解释

  1. 按键读取标志
    • if (key.read): 检查是否有新的按键读取请求。
    • key.read = 0;: 清除读取标志,准备下一次读取。
  2. 按键状态变化检测
    • if (key.readBuf != key.readBakBuf): 检查当前读取的按键值是否与备份值不同。
      • 如果不同,更新备份值并重置相关计数器和标志。
  3. 按键消抖处理
    • 如果按键值没有变化,增加消抖计数 key.delay++
    • 当消抖计数超过设定的阈值 key.ditherTimes 时,读取有效的按键值。
  4. 按键时间判断
    • 根据 key.accelMode 判断当前的计时方式,选择 key.newTimeskey.onTimes
    • 根据按键按下的总时间 ucBuf,调整按键读取的速度:
      • 高速 :如果按下时间超过 20,设置为高速模式。
      • 中速 :如果按下时间超过 10,设置为中速模式。
      • 低速 :恢复到正常操作速度,并设置防止连续处理的标志。

注意事项

  • 按键消抖 :确保 key.ditherTimes 的设置合理,以避免误触发。
  • 按键时间管理 :根据实际需求,调整 ucBuf 的判断条件,以适应不同的按键响应速度。
  • 状态管理 :确保 key 结构体的其他字段(如 accelModeonTimes 等)在其他部分代码中正确更新和使用。

三、按键解码

void KEY_ValueDecode(void)
{
    unsigned char i;
    unsigned char code;
    unsigned long buffer;
    if (key.decode)
    {
        key.decode = 0;
        if (key.validBuf)
        {
            code = 1; // 按键编码
            buffer = key.validBuf;
            while (!(buffer & 1))
            {
                buffer >>= 1;
                code++;
            }

            if (buffer >> 1)
            {for (i = 0; i < multipleKey; i++)
                {if (key.validBuf == pgm_read_word(keyCodeTable + i))
                    { // 有键按下
                        break;
                    }
                }
                code = 13 + i; // none 1bit + int 16bit   start 17bit
            } // 最后一个为无效按键
        }
        else
        {code = 0;}
        if (code > key.code)
        {                 // 组合键释放时静音
            key.tone = 0; // 清除按键音标志(允许再次发按键音)}
        key.code = code;
        key.prcs = 1;
    }
}
KEY_ValueDecode 函数用于解码按键输入并处理相应的操作

逐步解释

  1. 解码标志检查
    • if (key.decode): 检查是否有新的按键解码请求。
    • key.decode = 0;: 清除解码标志,以准备下一次解码。
  2. 有效按键检测
    • if (key.validBuf): 检查是否有有效的按键值。
    • code = 1;: 初始化按键编码为 1。
    • buffer = key.validBuf;: 将有效按键值保存到 buffer 中。
  3. 查找最低有效位
    • while (!(buffer & 1)): 通过右移操作查找最低有效位,计算按键编码。
  4. 检查多个按键
    • if (buffer >> 1): 检查是否有多个按键被按下。
    • 遍历 keyCodeTable,查找当前有效按键值是否存在。
    • 如果找到,更新 code13 + i,表示组合键。
  5. 音效处理
    • if (code > key.code): 如果当前按键编码大于之前的编码,清除音效标志 key.tone,允许再次发出按键音。
  6. 更新状态
    • key.code = code;: 更新当前按键编码。
    • key.prcs = 1;: 设置处理标志,表示有新的按键事件需要处理。

注意事项

  • 键码表 :确保 keyCodeTablemultipleKey 已正确初始化,并包含所有可能的按键值。
  • 音效管理 :根据具体需求,调整音效处理逻辑,以适应不同的按键交互模式。
  • 组合键支持 :确保组合键逻辑与实际硬件设计一致,避免误解码。

四、按键操作和相应功能

void KEY_FunAction(void)
{if (key.prcs)
    {
        key.prcs = 0; // 按键处理已响应
        if (!key.fallPrcs)
        { // 后沿按键 直接调用
            if (key.code)
            {
                key.state = ON; // 按键按下标志
                key.offTimes = 0x00;
                if (key.code < keyNumber)
                {key.restoreBuf = key.validBuf;}
                else
                { // 无效按键
                    key.code = keyNumber;
                    key.tone = 1; // 禁止按键没完全释放的发音
                }
            }
            else
            {
                key.state = OFF;
                key.onTimes = 0x00;
                key.restoreBuf = key.validBuf;
            }
        }
        CALL_FUN_PTR(pgm_read_word(KeyFunList + key.code)); // 按键处理函数调用
    }
}

KEY_FunAction 函数用于处理按键操作并调用相应的功能,

逐步解释

  1. 按键处理标志检查
    • if (key.prcs): 检查是否有新的按键事件需要处理。
    • key.prcs = 0;: 清除处理标志,表示按键事件已响应。
  2. 后沿按键处理
    • if (!key.fallPrcs): 检查是否为后沿按键(即按键释放时)。
    • 如果是后沿按键,继续处理按键状态。
  3. 按键状态判断
    • if (key.code): 检查按键编码是否有效。
      • 按键按下
        • 设置 key.state = ON;,表示按键处于按下状态。
        • 清零 key.offTimes,重置按键释放计时。
        • 如果按键编码小于 keyNumber,保存当前有效按键值到 key.restoreBuf
        • 如果按键编码无效,设置 key.codekeyNumber,并禁止发音(key.tone = 1;)。
      • 按键未按下
        • 设置 key.state = OFF;,表示按键未按下。
        • 清零 key.onTimes,重置按键按下计时。
        • 保存当前有效按键值到 key.restoreBuf
  4. 调用功能
    • CALL_FUN_PTR(pgm_read_word(KeyFunList + key.code));:根据按键编码调用相应的功能处理函数。

注意事项

  • 功能指针管理 :确保 KeyFunList 中的功能指针已正确初始化,且与按键编码一一对应。
  • 按键状态管理 :确保 key.statekey.onTimeskey.offTimes 的使用符合实际需求,以避免状态混乱。
  • 音效管理 :根据具体需求,调整音效处理逻辑,以适应不同的按键交互模式。

 

 

 

 

 

 

 

 

正文完
 0